#include "preHeader.h"
#include "MapServerMgr.h"
#include "MapServerSession.h"
#include "GateServerMgr.h"
#include "ServerMaster.h"
#include "Game/CharacterMgr.h"
#include "Game/TeleportMgr.h"

MapServerMgr::MapServerMgr()
{
}

MapServerMgr::~MapServerMgr()
{
}

void MapServerMgr::LoadMapServerConfig()
{
	const auto& cfg = IServerMaster::GetInstance().GetConfig();

	auto n = cfg.GetInteger("MAP_SERVER", "MAP_SERVER_COUNT", 1);
	m_MapServerInfos.resize(n + 1);

	std::ostringstream s;
	for (int i = 1; i <= n; ++i, s.str({})) {
		s << "MAP_SERVER_" << i;
		TextUnpacker unpacker(cfg.GetString(s.str().c_str(), "MAPS", ""));
		auto& mapCfgs = m_MapServerInfos[i].mapCfgs;
		while (!unpacker.IsEmpty()) {
			auto& mapIds = mapCfgs[unpacker.Unpack<uint32>()];
			while (!unpacker.IsEmpty() && !unpacker.IsDelimiter(';')) {
				auto mapId = unpacker.Unpack<uint32>();
				if (!unpacker.IsEmpty() && unpacker.IsDelimiter('-')) {
					auto endMapId = unpacker.Unpack<uint32>();
					for (; mapId <= endMapId; ++mapId) {
						mapIds.insert(mapId);
					}
				} else {
					mapIds.insert(mapId);
				}
			}
		}
	}
}

bool MapServerMgr::RegisterMapServer(uint32 hintServiceId, MapServerProxy* pProxy)
{
	auto pSession = pProxy->GetSession();
	auto serviceId = pSession->service();
	if (serviceId == 0) {
		if (hintServiceId != 0 && hintServiceId < m_MapServerInfos.size()) {
			auto pMapServerInfo = m_MapServerInfos[hintServiceId];
			if (pMapServerInfo.pProxy == NULL) {
				serviceId = hintServiceId;
			}
		}
		if (serviceId == 0) {
			for (size_t i = 1, n = m_MapServerInfos.size(); i < n; ++i) {
				auto pMapServerInfo = m_MapServerInfos[i];
				if (pMapServerInfo.pProxy == NULL) {
					serviceId = (u32)i;
					break;
				}
			}
		}
	}
	if (serviceId == 0) {
		WLOG("Can't find available map server serviceId.");
		return false;
	}
	if (serviceId >= m_MapServerInfos.size()) {
		WLOG("Invalid map server serviceId[%u].", serviceId);
		return false;
	}
	if (m_MapServerInfos[serviceId].pProxy != NULL) {
		WLOG("Map server proxy %u already exists.", serviceId);
		return false;
	}

	m_MapServerInfos[serviceId].pProxy = pProxy;
	m_MapServerMap.emplace(pSession->sn(), pProxy);
	pSession->SetServiceId(serviceId);

	NetPacket pck(SGG_ADD_MAP_SERVER);
	pSession->PackMapServerInfo(pck);
	sGateServerMgr.BroadcastPacket2AllGateServer(pck);

	NLOG("Server <%u> online!", serviceId);

	return true;
}

void MapServerMgr::RemoveMapServer(MapServerProxy* pProxy)
{
	auto pSession = pProxy->GetSession();
	auto serviceId = pSession->GetServiceId();
	if (serviceId == 0) {
		return;
	}

	NetPacket pck(SGG_REMOVE_MAP_SERVER);
	pck << pSession->sn();
	sGateServerMgr.BroadcastPacket2AllGateServer(pck);

	sCharacterMgr.DisposeMapServerOffline(pProxy);
	sTeleportMgr.DisposeMapServerOffline(pProxy);

	m_MapServerInfos[serviceId].pProxy = NULL;
	m_MapServerMap.erase(pSession->sn());
	pSession->SetServiceId(0);

	WLOG("Server <%u> offline!", serviceId);
}

void MapServerMgr::BroadcastServerId2AllMapServer(uint32 serverId) const
{
	NetPacket pck(GS_PUSH_SERVER_ID);
	pck << serverId;
	BroadcastPacket2AllMapServer(pck);
}

void MapServerMgr::BroadcastPacket2AllMapServer(const INetPacket& pck) const
{
	for (const auto& pair : m_MapServerMap) {
		pair.second->SendPacket(pck);
	}
}

void MapServerMgr::BroadcastPacket2AllMapServer(const INetPacket& pck, const INetPacket& data) const
{
	for (const auto& pair : m_MapServerMap) {
		pair.second->SendPacket(pck, data);
	}
}

void MapServerMgr::SendPacket2MapServer(uint32 serviceId, const INetPacket& pck) const
{
	auto pProxy = GetMapServer(serviceId);
	if (pProxy != NULL) {
		pProxy->SendPacket(pck);
	}
}

void MapServerMgr::SendPacket2MapServer(InstGUID instGuid, const INetPacket& pck) const
{
	auto pProxy = GetMapServer(instGuid);
	if (pProxy != NULL) {
		pProxy->SendPacket(pck);
	}
}

void MapServerMgr::SendPacket2MapServer(Character* pChar, const INetPacket& pck) const
{
	auto pProxy = GetMapServer(pChar->lastInstGuid);
	if (pProxy != NULL) {
		pProxy->SendPacket(pck);
	}
}

void MapServerMgr::RouteToInstance(InstGUID instGuid, const INetPacket& pck) const
{
	auto pProxy = GetMapServer(instGuid);
	if (pProxy != NULL) {
		pProxy->TransInstancePacket(instGuid, pck);
	}
}

void MapServerMgr::RouteToPlayer(Character* pChar, const INetPacket& pck) const
{
	auto pProxy = GetMapServer(pChar->lastInstGuid);
	if (pProxy != NULL) {
		pProxy->TransPlayerPacket(pChar->guid, pck);
	}
}

void MapServerMgr::RPCInvoke2Instance(InstGUID instGuid, const INetPacket& pck,
	const std::function<void(INetStream&, int32, bool)>& cb,
	AsyncTaskOwner* owner, time_t timeout) const
{
	auto pProxy = GetMapServer(instGuid);
	if (pProxy != NULL) {
		pProxy->TransRPCInvoke2InstancePacket(instGuid, pck, cb, owner, timeout);
	}
}

void MapServerMgr::RPCInvoke2Player(Character* pChar, const INetPacket& pck,
	const std::function<void(INetStream&, int32, bool)>& cb,
	AsyncTaskOwner* owner, time_t timeout) const
{
	auto pProxy = GetMapServer(pChar->lastInstGuid);
	if (pProxy != NULL) {
		pProxy->TransRPCInvoke2PlayerPacket(pChar->guid, pck, cb, owner, timeout);
	}
}

void MapServerMgr::RPCReply2Instance(InstGUID instGuid, const INetPacket& pck,
	uint64 sn, int32 err, bool eof) const
{
	auto pProxy = GetMapServer(instGuid);
	if (pProxy != NULL) {
		pProxy->TransRPCReply2InstancePacket(instGuid, pck, sn, err, eof);
	}
}

void MapServerMgr::RPCReply2Player(Character* pChar, const INetPacket& pck,
	uint64 sn, int32 err, bool eof) const
{
	auto pProxy = GetMapServer(pChar->lastInstGuid);
	if (pProxy != NULL) {
		pProxy->TransRPCReply2PlayerPacket(pChar->guid, pck, sn, err, eof);
	}
}

void MapServerMgr::PackAllMapServerInfos(INetPacket& pck) const
{
	pck << (u16)m_MapServerMap.size();
	for (const auto& pair : m_MapServerMap) {
		pair.second->GetSession()->PackMapServerInfo(pck);
	}
}

void MapServerMgr::StartWorldMapsOnServer(MapServerProxy* pProxy) const
{
	auto serviceId = pProxy->GetSession()->GetServiceId();
	auto StartWorldMap = [=](uint32 mapId) {
		NetPacket pack(GS_START_WORLD_MAP);
		pack << mapId;
		pProxy->SendPacket(pack);
	};

	auto pTable = sDBMgr.GetTable<MapInfo>();
	for (auto& pair : MakeIteratorRange(pTable->Begin(), pTable->End())) {
		auto pMapInfo = &pair.second;
		if (pMapInfo->type != (u32)MapInfo::Type::WorldMap) {
			continue;
		}
		const MapServerInfo* pMapServerInfo =
			GetMapServerInfo((u32)MapInfo::Type::WorldMap, pMapInfo->Id);
		if (pMapServerInfo->pProxy != pProxy) {
			continue;
		}
		StartWorldMap(pMapInfo->Id);
		NLOG("Map[%u] on server<%u> starting...", pMapInfo->Id, serviceId);
	}

	StartWorldMap(u32(-1));
	NLOG("All maps on server<%u> started!", serviceId);
}

void MapServerMgr::StartInstance(InstGUID fakeInstGuid,
	uint32 opCodeResp, uint32 flags, const std::string_view& args) const
{
	auto pProxy = GetMapServer(fakeInstGuid);
	if (pProxy == NULL) {
		WLOG("Start instance[%hu,%hu,%u] failed, map server proxy is offline.",
			fakeInstGuid.TID, fakeInstGuid.MAPID, fakeInstGuid.UID);
		return;
	}

	NetPacket pack(GS_START_INSTANCE);
	pack << fakeInstGuid << opCodeResp << flags;
	pack.Append(args.data(), args.size());
	pProxy->SendPacket(pack);
}

void MapServerMgr::StopInstance(InstGUID instGuid) const
{
	auto pProxy = GetMapServer(instGuid);
	if (pProxy == NULL) {
		WLOG("Stop instance[%hu,%hu,%u] failed, map server proxy is offline.",
			instGuid.TID, instGuid.MAPID, instGuid.UID);
		return;
	}

	NetPacket pack(GS_STOP_INSTANCE);
	pack << instGuid;
	pProxy->SendPacket(pack);
}

MapServerProxy* MapServerMgr::GetMapServer(uint32 serviceId) const
{
	return m_MapServerInfos.size() > serviceId ?
		m_MapServerInfos[serviceId].pProxy : NULL;
}

MapServerProxy* MapServerMgr::GetMapServer(InstGUID instGuid) const
{
	auto pMapServerInfo = GetMapServerInfo(instGuid.TID, instGuid.MAPID);
	return pMapServerInfo != NULL ? pMapServerInfo->pProxy : NULL;
}

const MapServerMgr::MapServerInfo*
MapServerMgr::GetMapServerInfo(uint32 mapType, uint32 mapId) const
{
	size_t service = 0;
	for (size_t i = 1, n = m_MapServerInfos.size(); i < n; ++i) {
		auto& mapCfgs = m_MapServerInfos[i].mapCfgs;
		auto itr = mapCfgs.find(mapType);
		if (itr == mapCfgs.end()) {
			continue;
		}
		const auto& mapIds = itr->second;
		if (mapIds.count(mapId) != 0) {
			service = i;
			break;
		}
		if (mapIds.empty()) {
			service = i;
		}
	}
	return &m_MapServerInfos[service];
}
